package br.com.winesys.geta.databinding.component.selector;

import java.awt.Component;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.event.FocusAdapter;
import java.awt.event.FocusEvent;
import java.awt.event.KeyEvent;
import java.beans.PropertyChangeEvent;
import java.beans.PropertyChangeListener;
import java.io.Serializable;
import java.util.List;
import java.util.logging.Level;
import java.util.logging.Logger;
import javax.swing.BorderFactory;
import javax.swing.GroupLayout;
import javax.swing.GroupLayout.Alignment;
import javax.swing.ImageIcon;
import javax.swing.JButton;
import javax.swing.JComponent;
import javax.swing.JLabel;
import javax.swing.JOptionPane;
import javax.swing.JPanel;
import javax.swing.JTextField;
import javax.swing.KeyStroke;
import javax.swing.LayoutStyle.ComponentPlacement;
import javax.swing.SwingUtilities;
import javax.swing.border.EtchedBorder;
import org.apache.commons.beanutils.PropertyUtils;
import br.com.winesys.geta.databinding.component.listeners.SelectionListener;
import br.com.winesys.geta.databinding.component.table.TableModel;

public abstract class ObjectSelector<Entity> extends JPanel {

	private JTextField keyText;
	private JButton findButton;
	private JLabel displayLabel;
	private String displayProperty;
	private String keyProperty;
	private Class<?> keyClass;
	private Serializable lastLoadedKey = null;
	private Entity selectedObject;
	private TableModel<Entity> tableModel;
	private ObjectLocator<Entity> objectLocator;

	public ObjectSelector() {
		super();
		this.displayProperty = this.getDisplayProperty();
		this.keyProperty = this.getKeyProperty();
		this.keyClass = this.getKeyClass();
		this.tableModel = this.getTableModel();
		this.objectLocator = new ObjectLocator<Entity>(SwingUtilities.getWindowAncestor(this), new SelectionListener<Entity>() {

			@Override
			public void itemSelected(Entity object) {
				if (object != null) {
					try {
						Serializable key = (Serializable) PropertyUtils.getProperty(object, ObjectSelector.this.keyProperty);
						if (key != null) {
							ObjectSelector.this.load(key);
						}
					} catch (Exception ex) {
						Logger.getLogger(getClass().getName()).log(Level.SEVERE, null, ex);
					}
				}
			}
		}) {

			@Override
			protected TableModel<Entity> createTableModel() {
				return ObjectSelector.this.tableModel;
			}

			@Override
			protected void getValues() {
				try {
					ObjectSelector.this.listSelectable();
				} catch (Exception e) {
					JOptionPane.showMessageDialog(SwingUtilities.getRoot(ObjectSelector.this), e.getMessage(), e.getClass().getName(), JOptionPane.ERROR_MESSAGE);
				}
			}
		};

		this.addFocusListener(new FocusAdapter() {

			@Override
			public void focusGained(FocusEvent evt) {
				ObjectSelector.this.keyText.requestFocus();
			}
		});
		this.registerKeyboardAction(new ActionListener() {

			@Override
			public void actionPerformed(ActionEvent e) {
				ObjectSelector.this.load();
			}
		}, KeyStroke.getKeyStroke(KeyEvent.VK_ENTER, 0), JComponent.WHEN_ANCESTOR_OF_FOCUSED_COMPONENT);
		this.registerKeyboardAction(new ActionListener() {

			@Override
			public void actionPerformed(ActionEvent e) {
				ObjectSelector.this.find();
			}
		}, KeyStroke.getKeyStroke(KeyEvent.VK_F9, 0), JComponent.WHEN_ANCESTOR_OF_FOCUSED_COMPONENT);
		this.addPropertyChangeListener(new PropertyChangeListener() {

			@Override
			public void propertyChange(PropertyChangeEvent evt) {
				if (evt.getPropertyName().equalsIgnoreCase("enabled")) {
					for (Component c : getComponents()) {
						c.setEnabled((Boolean) evt.getNewValue());
					}
				}
			}
		});
		this.keyText = new JTextField(5);
		this.keyText.addFocusListener(new FocusAdapter() {

			@Override
			public void focusLost(FocusEvent evt) {
				if (!evt.isTemporary()) {
					ObjectSelector.this.load();
				}
			}
		});
		this.findButton = new JButton(new ImageIcon(ObjectSelector.class.getResource("/images/search.png")));
		this.findButton.setFocusable(false);
		this.findButton.addActionListener(new ActionListener() {

			@Override
			public void actionPerformed(ActionEvent evt) {
				ObjectSelector.this.find();
			}
		});
		this.displayLabel = new JLabel();
		this.displayLabel.setBorder(BorderFactory.createEtchedBorder(EtchedBorder.RAISED));
		GroupLayout layout = new GroupLayout(this);
		this.setLayout(layout);
		layout.setHorizontalGroup(layout.createParallelGroup(Alignment.LEADING).addGroup(layout.createSequentialGroup().addComponent(this.keyText, GroupLayout.PREFERRED_SIZE, GroupLayout.DEFAULT_SIZE, GroupLayout.PREFERRED_SIZE).addComponent(this.findButton, GroupLayout.PREFERRED_SIZE, this.keyText.getPreferredSize().height, this.keyText.getPreferredSize().height).addPreferredGap(ComponentPlacement.RELATED).addComponent(this.displayLabel, 0, 150, Short.MAX_VALUE)));
		layout.setVerticalGroup(layout.createParallelGroup(Alignment.LEADING).addComponent(this.keyText).addComponent(this.displayLabel, GroupLayout.DEFAULT_SIZE, this.keyText.getPreferredSize().height, Short.MAX_VALUE).addComponent(this.findButton, 0, 0, Short.MAX_VALUE));
	}

	@Override
	public void setEnabled(boolean enabled) {
		super.setEnabled(enabled);
		this.keyText.setEditable(enabled);
		this.keyText.setFocusable(enabled);
		this.findButton.setEnabled(enabled);
	}

	@Override
	public void setFocusable(boolean focusable) {
		super.setFocusable(focusable);
		this.keyText.setFocusable(focusable);
		this.findButton.setEnabled(focusable);
	}

	public Object getSelected() {
		return this.selectedObject;
	}

	public void setSelected(Entity object) {
		this.selectedObject = object;
		if (this.selectedObject != null) {
			try {
				this.lastLoadedKey = (Serializable) PropertyUtils.getProperty(this.selectedObject, this.keyProperty);
				if (this.lastLoadedKey != null) {
					this.keyText.setText(this.lastLoadedKey.toString());
					this.displayLabel.setText(PropertyUtils.getProperty(this.selectedObject, this.displayProperty).toString());
				} else {
					this.keyText.setText("");
					this.displayLabel.setText("");
				}
			} catch (Exception e) {
				Logger.getLogger(getClass().getName()).log(Level.SEVERE, null, e);
			}
		} else {
			this.lastLoadedKey = null;
			this.keyText.setText("");
			this.displayLabel.setText("");
		}
		for (SelectionListener<Entity> listener : this.listenerList.getListeners(SelectionListener.class)) {
			listener.itemSelected(object);
		}
	}

	public void addSelectionListener(SelectionListener<Entity> listener) {
		this.listenerList.add(SelectionListener.class, listener);
	}

	public void removeSelectionListener(SelectionListener<Entity> listener) {
		this.listenerList.remove(SelectionListener.class, listener);
	}

	private void find() {
		this.objectLocator.run();
	}

	protected void load() {
		try {
			Serializable key = null;
			if (this.keyText.getText().trim().isEmpty()) {
				this.setSelected(null);
			} else if (this.keyClass.equals(Integer.class)) {
				key = Integer.parseInt(this.keyText.getText().trim());
			} else if (this.keyClass.equals(Long.class)) {
				key = Long.parseLong(this.keyText.getText().trim());
			} else {
				key = this.keyText.getText();
			}
			if ((this.lastLoadedKey != null && this.lastLoadedKey.equals(key)) || (this.lastLoadedKey == null && key == null)) {
				return;
			}
			this.load(key);
		} catch (Exception ex) {
			this.setSelected(null);
		}
	}

	public void itemsListed(List<Entity> items) {
		this.objectLocator.setList(items);
	}

	protected void itemLoaded(Entity item) {
		this.setSelected(item);
	}

	public abstract void listSelectable();

	public abstract void load(Serializable id);

	public abstract String getDisplayProperty();

	public abstract String getKeyProperty();

	public abstract Class<?> getKeyClass();

	public abstract TableModel<Entity> getTableModel();
}